package com.dynamicthreadpool.spi.support;

import com.dynamicthreadpool.spi.URL;

import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 带报告的拒绝策略
 * 从Dubbo 2.7版本提取的完整实现
 */
public class AbortPolicyWithReport implements RejectedExecutionHandler {

    protected static final String DEFAULT_DIRECTORY = System.getProperty("user.home") + "/logs/dubbo";

    private static final AtomicBoolean WARNED = new AtomicBoolean();
    private static volatile long lastPrintTime = 0;
    private static final long WARN_INTERVAL = 60 * 1000; // 1 minute

    private final String threadName;
    private final URL url;
    private final String dumpDir;
    private final String dumpPrefix;

    public AbortPolicyWithReport(String threadName, URL url) {
        this(threadName, url, null, null);
    }

    public AbortPolicyWithReport(String threadName, URL url, String dumpDir, String dumpPrefix) {
        this.threadName = threadName;
        this.url = url;
        this.dumpDir = dumpDir == null ? url.getParameter("dump.directory", DEFAULT_DIRECTORY) : dumpDir;
        this.dumpPrefix = dumpPrefix == null ? url.getParameter("dump.prefix", "dubbo") : dumpPrefix;
    }

    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        String msg = String.format("Thread pool is EXHAUSTED! Thread Name: %s, Pool Size: %d (active: %d, core: %d, max: %d, largest: %d), Task: %d (completed: %d), Executor status:(isShutdown:%s, isTerminated:%s, isTerminating:%s), in %s://%s:%d!",
                threadName, e.getPoolSize(), e.getActiveCount(), e.getCorePoolSize(), e.getMaximumPoolSize(), e.getLargestPoolSize(),
                e.getTaskCount(), e.getCompletedTaskCount(), e.isShutdown(), e.isTerminated(), e.isTerminating(),
                url.getProtocol(), url.getHost(), url.getPort());
        logger.warn(msg);
        dumpJStack();
        throw new RejectedExecutionException(msg);
    }

    private void dumpJStack() {
        // 限制每分钟最多打印一次警告
        long now = System.currentTimeMillis();
        if (now - lastPrintTime < WARN_INTERVAL) {
            return;
        }
        if (!WARNED.compareAndSet(false, true)) {
            return;
        }

        try {
            File dir = new File(dumpDir);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            Date date = new Date();
            String dumpPath = dumpDir + File.separator + dumpPrefix + ".threaddump." + new SimpleDateFormat("yyyyMMddHHmmss").format(date);

            StringBuilder jstack = new StringBuilder();
            for (Thread thread : Thread.getAllStackTraces().keySet()) {
                Thread.State state = thread.getState();
                jstack.append("\n");
                jstack.append("Thread: ").append(thread.getName()).append("(id: ").append(thread.getId()).append(")\n");
                jstack.append("\tState: ").append(state).append("\n");
                StackTraceElement[] stackTraceElements = thread.getStackTrace();
                for (StackTraceElement element : stackTraceElements) {
                    jstack.append("\t\tat ").append(element).append("\n");
                }
            }

            // 这里简化实现，实际应该写入文件
            System.err.println("JStack dump saved to: " + dumpPath);
            System.err.println(jstack.toString());

        } catch (Throwable t) {
            logger.warn("Dump jstack error", t);
        } finally {
            WARNED.set(false);
            lastPrintTime = System.currentTimeMillis();
        }
    }

    // 简化的日志实现
    private static class Logger {
        public void warn(String msg) {
            System.err.println("WARN: " + msg);
        }

        public void warn(String msg, Throwable t) {
            System.err.println("WARN: " + msg);
            if (t != null) {
                t.printStackTrace();
            }
        }
    }

    private static final Logger logger = new Logger();
}